#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/epoll.h>

typedef void *(*Callback)(int fd, __uint32_t events);

typedef struct EventAction_t {
	__uint32_t events;
	Callback cb;
	struct EventAction_t *next;
}EventAction;

typedef struct {
	int active;
	__uint32_t events;
	EventAction *head;
}FdRecord;

struct eventloop {
	int epfd;
	int maxevent;
	FdRecord *fdrecord;
	struct epoll_event *events;
	//one loop per thread????
};

struct eventloop *Create(/*struct eventloop *loop,*/ int maxevent) {
	struct eventloop *loop;
	loop = (struct eventloop *)malloc(sizeof(struct eventloop));
	loop->maxevent = maxevent;
	loop->epfd = epoll_create1(0);
	if(loop->epfd == -1) {
		return -1;
	}
	loop->events = (struct epoll_event *)malloc(maxevent * sizeof(struct epoll_event));
	//init
	loop->fdrecord = (FdRecord *)malloc(maxevent * sizeof(FdRecord));
	int i;
	for(i=0; i<maxevent; i++) {
		loop->fdrecord[i].active = 0;
		loop->fdrecord[i].events = 0;
		loop->fdrecord[i]. head = NULL;
	}
	return loop;
}

int Register(struct eventloop *loop, int fd, __uint32_t events, Callback cb) {
	//printf("%d\n", loop->fdrecord[fd].events);

	if((loop->fdrecord[fd].events & events) == events) { /*events registerd already, just change the cb*/
		printf("ev exist \n");
		//search the EventAction list to change the callback func
		EventAction *tmp = loop->fdrecord[fd].head;
		while(tmp != NULL) {
			if(tmp->events & events) { /*include the events*/
				tmp->cb = cb;
				break;
			}
			tmp = tmp->next;
		}
	} else { /*have new add event(s)*/
		EventAction *newaction = (EventAction *)malloc(sizeof(EventAction));
		newaction->events = events;
		newaction->cb = cb;
		//newaction->next = NULL;

		//insert in the head of the list
		EventAction *tmp = loop->fdrecord[fd].head;
		loop->fdrecord[fd].head = newaction;
		newaction->next = tmp;

		loop->fdrecord[fd].events |= events;
		struct epoll_event ev;
		ev.events = loop->fdrecord[fd].events;
		ev.data.fd = fd;
		if(loop->fdrecord[fd].active) {
			printf("mod\n");
			epoll_ctl(loop->epfd, EPOLL_CTL_MOD, fd, &ev);
		} else {
			printf("add\n");
			epoll_ctl(loop->epfd, EPOLL_CTL_ADD, fd, &ev);
		}
	}
	
		

	loop->fdrecord[fd].active = 1;
	return 0;
}

/*
* Unregister the fd from the epoll Or 
* just unregist the fd's one or several events
*/
int Unregister(struct eventloop *loop, int fd, __uint32_t events) {
	if(events == 0) { /*Unregister the fd*/
		struct epoll_event ev;
		epoll_ctl(loop->epfd, EPOLL_CTL_DEL, fd, &ev);
	} else {

	}
	return 0;
}
void  *test2(int fd, __uint32_t events);
int Run(struct eventloop *loop) {
	printf("efd:%d\n", loop->epfd);
	while(1) {
		int num;
		num = epoll_wait(loop->epfd, loop->events, loop->maxevent, -1);
		if(num == -1) {
			fprintf(stderr, "epoll wait error\n");
			return -1;
		}
		int i;
		for(i=0; i<num; i++) {
			int fd = loop->events[i].data.fd;
			EventAction *tmp = loop->fdrecord[fd].head;
			while(tmp != NULL) {
				if(tmp->events & loop->events[i].events) { /*include the events*/
					(*(tmp->cb))(fd, loop->events[i].events);
					//Unregister(loop, fd, 0);
					Register(loop, 0, EPOLLIN, test2);
					//Unregister(loop, fd, 0);
					break;
				}
				tmp = tmp->next;
			}
			//sleep(3);
		}
	}
}

void *test(int fd, __uint32_t events) {
	printf("***fd:%d, events:%x\n", fd, events);
	return NULL;
}
void *test2(int fd, __uint32_t events) {
	printf("+++fd:%d, events:%x\n", fd, events);
	return NULL;
}
int main()
{
	struct eventloop *loop = Create(20);
	//Create(loop, 20);
	if(loop == NULL) {
		printf("error: loop is null\n");
		return -1;
	}
	Register(loop, 0, EPOLLIN|EPOLLET, test);
	Run(loop);

	return 0;
}